package cmd

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"gitee.com/qianyucc/secure-tool/util"
	"github.com/spf13/cobra"
)

var (
	UseRawStdEncoding bool
	UseUrlEncoding    bool
	UseRawURLEncoding bool
)

// base64Cmd Base64编码
var base64Cmd = &cobra.Command{
	Use:   "base64",
	Short: "Use Base64 to encode and decode strings or files",
	Long: `Use Base64 to encode and decode strings or files
	The maximum file size is 10M.
	If you don't enter any text, it will read text from your system clipboard.`,
	Run: func(cmd *cobra.Command, args []string) {
		if err := cmd.ParseFlags(args); err != nil {
			fmt.Println(err)
			return
		}
		base64Encoder := base64.StdEncoding
		if UseRawStdEncoding {
			base64Encoder = base64.RawStdEncoding
		} else if UseUrlEncoding {
			base64Encoder = base64.URLEncoding
		} else if UseRawURLEncoding {
			base64Encoder = base64.RawURLEncoding
		}
		if IsDecode {
			if err := DecodeFileOrStringWithBase64(args, base64Encoder); err != nil {
				fmt.Println(err)
			}
		} else {
			if err := EncodeFileOrStringWithBase64(args, base64Encoder); err != nil {
				fmt.Println(err)
			}
		}
	},
}

//EncodeFileOrStringWithBase64 编码文件或者字符串
func EncodeFileOrStringWithBase64(args []string, base64Encoder *base64.Encoding) (err error) {
	if InputFile == "" {
		// 处理简单文字
		var text string
		if text = util.ReadTextFromClipboard(); len(args) == 0 && text == "" {
			return fmt.Errorf("invalid parameters, please enter the string you want to encode")
		}
		if len(args) > 0 {
			text = args[0]
		}
		if len(strings.Trim(text, " ")) == 0 {
			fmt.Println("")
			return nil
		}
		r := base64Encoder.EncodeToString([]byte(text))
		util.PrintAndCopyTextToClipboardIgnoreError(r)
		return nil
	}
	// 处理文件
	if err = CheckFileSize(InputFile); err != nil {
		return err
	}
	// 如果没有输出文件名，就使用默认文件名
	OutputFile = util.GetOrDefaultString(OutputFile, fmt.Sprintf("%s-base64-enc.txt", InputFile))
	return EncodeFileWithBase64(base64Encoder, InputFile, OutputFile)
}

// EncodeFileWithBase64 编码文件
func EncodeFileWithBase64(base64Encoder *base64.Encoding, inputFilePath, outputFilePath string) (err error) {
	var raw []byte
	if raw, err = ioutil.ReadFile(inputFilePath); err != nil {
		return err
	}
	base64Str := base64Encoder.EncodeToString(raw)
	return ioutil.WriteFile(outputFilePath, []byte(base64Str), 0644)
}

// CheckFileSize 检查文件大小是否超出固定大小
func CheckFileSize(filePath string) (err error) {
	var fileInfo os.FileInfo
	if fileInfo, err = os.Stat(filePath); err != nil {
		return err
	}
	sz := fileInfo.Size()
	if sz > MAX_FILE_SIZE {
		return fmt.Errorf("the file size is too large, max file size is %dM", MAX_FILE_SIZE/1024/1024)
	}
	return nil
}

// DecodeFileOrStringWithBase64 解码文件或字符串
func DecodeFileOrStringWithBase64(args []string, base64Encoder *base64.Encoding) (err error) {
	if InputFile == "" && OutputFile == "" {
		// 解码文字
		var text string
		if text = util.ReadTextFromClipboard(); len(args) == 0 && text == "" {
			return fmt.Errorf("invalid parameters, please enter the string you want to decode")
		}
		if len(args) > 0 {
			text = args[0]
		}
		var raw []byte
		if raw, err = base64Encoder.DecodeString(text); err != nil {
			return err
		}
		util.PrintAndCopyTextToClipboardIgnoreError(string(raw))
		return nil
	}
	if InputFile == "" {
		return fmt.Errorf("invalid parameters, please enter the file name you want to decode")
	}
	OutputFile = util.GetOrDefaultString(OutputFile, fmt.Sprintf("%s-base64-dec.txt", InputFile))
	// 解码文件
	return DecodeFileWithBase64(base64Encoder, InputFile, OutputFile)
}

// DecodeFileWithBase64 解码文件
func DecodeFileWithBase64(base64Encoder *base64.Encoding, inputFilePath, outputFilePath string) (err error) {
	var raw []byte
	if raw, err = ioutil.ReadFile(inputFilePath); err != nil {
		return err
	}
	var result []byte
	if result, err = base64Encoder.DecodeString(string(raw)); err != nil {
		return err
	}
	return ioutil.WriteFile(outputFilePath, result, 0644)
}

func init() {
	rootCmd.AddCommand(base64Cmd)
	base64Cmd.Flags().StringVarP(&InputFile, "file", "f", "", "The file name you want to encode or decode")
	base64Cmd.Flags().StringVarP(&OutputFile, "out", "o", "", `The file name after encoding or decoding. 
	When encoding, if you do not enter this flag, the output file will be named as ${fileName}-base64-enc.txt by default,
	When decoding, if you do not enter this flag, the output file will be named as ${fileName}-base64-dec.txt by default`)
	base64Cmd.Flags().BoolVarP(&IsDecode, "decode", "d", false, "Indicates whether it is a decoding operation")
	base64Cmd.Flags().BoolVarP(&UseRawStdEncoding, "raw", "r", false, "Use Base64 encoding without padding")
	base64Cmd.Flags().BoolVarP(&UseUrlEncoding, "url", "u", false, "Use Base64 encoding with UrlEncoding")
	base64Cmd.Flags().BoolVarP(&UseRawURLEncoding, "urlRaw", "a", false, "Use Base64 encoding with UrlEncoding without padding")
}
